package cn.iocoder.yudao.module.iot.controller;

import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.deviceeventslog.vo.DeviceEventsLogExportReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.deviceeventslog.vo.DeviceEventsLogPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicerel.vo.DeviceRelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicerel.vo.DeviceRelSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.phonemsglog.vo.PhoneMsgLogPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.phonemsglog.vo.PhoneMsgLogSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.warnpolicy.vo.WarnPolicyPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.warnpolicy.vo.WarnPolicySaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.deviceeventslog.DeviceEventsLogDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicerel.DeviceRelDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.phonemsglog.PhoneMsgLogDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.warnpolicy.WarnPolicyDO;
import cn.iocoder.yudao.module.iot.enums.Channel;
import cn.iocoder.yudao.module.iot.service.deviceeventslog.DeviceEventsLogService;
import cn.iocoder.yudao.module.iot.service.devicerel.DeviceRelService;
import cn.iocoder.yudao.module.iot.service.phonemsglog.PhoneMsgLogService;
import cn.iocoder.yudao.module.iot.service.warnpolicy.WarnPolicyService;
import cn.iocoder.yudao.module.iot.util.AmqpDataDownUtil;
import cn.iocoder.yudao.module.iot.util.SingleCallByTts;
import cn.iocoder.yudao.module.iot.vo.*;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.utils.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;

@RestController
@RequestMapping("/admin-api/iot")
@Tag(name = "iot模块")
@Validated
public class IotController {

    private final static Logger logger = LoggerFactory.getLogger(IotController.class);


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private ListOperations<String, String> listRedis;

    @Resource
    private DeviceEventsLogService deviceEventsLogService;

    @Resource
    private DeviceRelService deviceRelService;

    @Resource
    private WarnPolicyService warnPolicyService;

    @Resource
    private PhoneMsgLogService phoneMsgLogService;

    @PostConstruct
    private void init() {
        listRedis = stringRedisTemplate.opsForList();
    }

    @GetMapping("/get")
    public CommonResult<String> get() {
        return success("true");
    }


    // 获取设备状态--召读
    @Operation(summary = "召读-获取设备端数据")
    @RequestMapping("getDeviceStatus")
    @ResponseBody
    public Object getDeviceStatus(@RequestParam("deviceName") String deviceName){
        // 服务端下发指令，要求设备端上报设备状态数据
        Map<String,Object> resultMap=new HashMap<>();
        String id="requestDeviceStatus" + System.currentTimeMillis();
        IotBo iotBo = new IotBo();
        iotBo.setMethod("requestDeviceStatus");
        iotBo.setId(id);
        iotBo.setVersion("1.0.0");
        String jsonData = JSONObject.toJSON(iotBo).toString();
        logger.info("服务端下发的数据是:"+jsonData);
        boolean call = AmqpDataDownUtil.serverDataDown(deviceName,jsonData );
        long startTime = System.currentTimeMillis();
        if(call){ // 指令下发成功
            String message="";
            for(int i=0;i<10;i++){
                // 监听设备端返回的消息
                try {
                    Thread.sleep(1000);
                    message = listRedis.rightPop(Channel.UPLOAD_DEVICE_STATUS_CHANNEL+id);
                    if(!StringUtils.isEmpty(message)){
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            logger.info("召读设备回复耗时:"+costTime+"毫秒");
            logger.info("接收到的消息是:"+message);
            if(!StringUtils.isEmpty(message)){
                logger.info("开始解析设备端上报的消息:"+message);
                // 创建ObjectMapper对象
                ObjectMapper objectMapper = new ObjectMapper();
                // 解析JSON字符串为Java对象
                try {
                    DeviceRepsonseVo response = objectMapper.readValue(message, DeviceRepsonseVo.class);
                    if(id.equals(response.getId())){// 回复id一致
                        logger.info("回复的id与服务端一致");
                        resultMap.put("code",0);
                        resultMap.put("data",response);
                        resultMap.put("costTime",costTime);
                        return resultMap;
                    }else{
                        logger.info("警告：回复的id与服务端不一致:");
                        resultMap.put("code",1);
                        return resultMap;
                    }
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }else {
                resultMap.put("code",1);
                return resultMap;
            }
        }else{
            resultMap.put("code",1);
            return resultMap;
        }
    }


    // 打开或关闭阀门/排风扇
    @RequestMapping("changeZt")
    @ResponseBody
    public Object changeZt(@RequestParam("deviceName") String deviceName,
                           @RequestParam("zt") String zt,
                           @RequestParam("type") String type){
        IotBo iotBo = new IotBo();
        iotBo.setMethod("changeZt");
        String id="openQf" + System.currentTimeMillis();
        iotBo.setId(id);
        iotBo.setVersion("1.0.0");
        Params params = new Params();
        if("0".equals(type)){// 风扇
            if("1".equals(zt)){ //开启
                params.setPqzt((byte) 1);
            }else if("0".equals(zt)){ // 关闭
                params.setPqzt((byte) 0);
            }
        }else if("1".equals(type)){// 气阀
            if("1".equals(zt)){ //开启
                params.setQfzt((byte) 1);
            }else if("0".equals(zt)){ // 关闭
                params.setQfzt((byte) 0);
            }
        }

        iotBo.setParams(params);
        String jsonData = JSONObject.toJSON(iotBo).toString();
        logger.info("开关风扇排气阀下发的数据是："+jsonData);
        boolean call = AmqpDataDownUtil.serverDataDown(deviceName,jsonData );
        Map<String,Object> resultMap=new HashMap<>();
        if(call){ //指令下发成功
            long startTime = System.currentTimeMillis();
            String message="";
            for(int i=0;i<10;i++){
                // 监听设备端返回的消息
                try {
                    Thread.sleep(1000);
                    message = listRedis.rightPop(Channel.DEVICE_ZT_CHANGE_CHANNEL+id);
                    if(!StringUtils.isEmpty(message)){
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            logger.info("打开关闭气阀/排风扇设备回复耗时:"+costTime+"毫秒");
            logger.info("打开关闭气阀/排风扇--接收到的消息是:"+message);
            if(!StringUtils.isEmpty(message)){
                logger.info("打开关闭气阀--开始解析设备端上报的消息:"+message);
                // 创建ObjectMapper对象
                ObjectMapper objectMapper = new ObjectMapper();
                // 解析JSON字符串为Java对象
                try {
                    DeviceRepsonseVo response = objectMapper.readValue(message, DeviceRepsonseVo.class);
                    if(id.equals(response.getId())){// 回复id一致
                        logger.info("打开关闭气阀--回复的id与服务端一致");
                        resultMap.put("code",0);
                        resultMap.put("data",response);
                        resultMap.put("costTime",costTime);
                        return resultMap;
                    }else{
                        logger.info("打开关闭气阀--警告：回复的id与服务端不一致:");
                        resultMap.put("code",1);
                        return resultMap;
                    }
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }else {
                resultMap.put("code",1);
                return resultMap;
            }
        }else{
            resultMap.put("code",1);
            return resultMap;
        }
    }


    // 获取设备状态
    @RequestMapping("biaoding")
    @ResponseBody
    public Object biaoding(@RequestParam("deviceName") String deviceName,
                           @RequestParam("type") String type){
        // 服务端下发指令，要求设备端上报设备状态数据
        Map<String,Object> resultMap=new HashMap<>();
        String id="calibration" + System.currentTimeMillis();
        BiaodingBo iotBo = new BiaodingBo();
        iotBo.setMethod("calibration");
        iotBo.setId(id);
        iotBo.setVersion("1.0.0");
        BiaodingParams params=new BiaodingParams();
        params.setBiaoding(type);
        iotBo.setParams(params);
        String jsonData = JSONObject.toJSON(iotBo).toString();
        logger.info("设备标定--服务端下发的数据是:"+jsonData);
        boolean call = AmqpDataDownUtil.serverDataDown(deviceName,jsonData );
        if(call){ // 指令下发成功
            long startTime = System.currentTimeMillis();
            String message="";
            for(int i=0;i<10;i++){
                // 监听设备端返回的消息
                try {
                    Thread.sleep(1000);
                    message = listRedis.rightPop(Channel.DEVICE_BIAODING_CHANNEL+id);
                    if(!StringUtils.isEmpty(message)){
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            logger.info("数据标定设备回复耗时:"+costTime+"毫秒");
            logger.info("数据标定接收到的消息是:"+message);
            if(!StringUtils.isEmpty(message)){
                logger.info("开始解析设备端上报的消息:"+message);
                // 创建ObjectMapper对象
                ObjectMapper objectMapper = new ObjectMapper();
                // 解析JSON字符串为Java对象
                try {
                    DeviceRepsonseVo response = objectMapper.readValue(message, DeviceRepsonseVo.class);
                    if(id.equals(response.getId())){// 回复id一致
                        logger.info("设备标定回复的id与服务端一致");
                        resultMap.put("code",0);
                        resultMap.put("data",response);
                        return resultMap;
                    }else{
                        logger.info("设备标定警告：回复的id与服务端不一致:");
                        resultMap.put("code",1);
                        return resultMap;
                    }
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }else {
                resultMap.put("code",1);
                return resultMap;
            }
        }else{
            resultMap.put("code",1);
            return resultMap;
        }
    }


    // 召读--获取设备定位信息
    @RequestMapping("getDeviceLocation")
    @ResponseBody
    public Object getDeviceLocation(@RequestParam("deviceName") String deviceName){
        // 服务端下发指令，要求设备端上报设备状态数据
        Map<String,Object> resultMap=new HashMap<>();
        String id="getLocation" + System.currentTimeMillis();
        IotBo iotBo = new IotBo();
        iotBo.setMethod("getLocation");
        iotBo.setId(id);
        iotBo.setVersion("1.0.0");
        String jsonData = JSONObject.toJSON(iotBo).toString();
        logger.info("getLocation服务端下发的数据是:"+jsonData);
        boolean call = AmqpDataDownUtil.serverDataDown(deviceName,jsonData );
        long startTime = System.currentTimeMillis();
        if(call){ // 指令下发成功
            String message="";
            for(int i=0;i<10;i++){
                // 监听设备端返回的消息
                try {
                    Thread.sleep(1000);
                    message = listRedis.rightPop(Channel.UPLOAD_DEVICE_LOCATION_CHANNEL+id);
                    if(!StringUtils.isEmpty(message)){
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            logger.info("getLocation召读获取设备北斗定位回复耗时:"+costTime+"毫秒");
            logger.info("getDeviceLocation接收到的消息是:"+message);
            if(!StringUtils.isEmpty(message)){
                logger.info("getDeviceLocation开始解析设备端上报的消息:"+message);
                // 创建ObjectMapper对象
                ObjectMapper objectMapper = new ObjectMapper();
                // 解析JSON字符串为Java对象
                try {
                    DeviceGetLocationRepsonseVo response = objectMapper.readValue(message, DeviceGetLocationRepsonseVo.class);
                    if(id.equals(response.getId())){// 回复id一致
                        logger.info("getLocation回复的id与服务端一致");
                        resultMap.put("code",0);
                        resultMap.put("data",response);
                        resultMap.put("costTime",costTime);
                        return resultMap;
                    }else{
                        logger.info("getLocation警告：回复的id与服务端不一致:");
                        resultMap.put("code",1);
                        return resultMap;
                    }
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }else {
                resultMap.put("code",1);
                return resultMap;
            }
        }else{
            resultMap.put("code",1);
            return resultMap;
        }
    }


    //@Operation(summary = "获取设备状态历史")
    @RequestMapping("getDeviceLog")
    @ResponseBody
    public Object getDeviceLog(@RequestParam("deviceName") String deviceName){
        DeviceEventsLogPageReqVO vo=new DeviceEventsLogPageReqVO();
        vo.setDeviceName(deviceName);
        vo.setPageNo(1);
        vo.setPageSize(50);
        PageResult<DeviceEventsLogDO> logPage = deviceEventsLogService.getDeviceEventsLogPage(vo);
        List<DeviceEventsLogDO> pageList = logPage.getList();
        return success(pageList);
    }


    @Operation(summary = "获取用户绑定的设备列表")
    @RequestMapping("getUserDeviceList")
    @ResponseBody
    public Object getUserDeviceList(@RequestParam("userId") Integer userId){
        DeviceRelPageReqVO pageReqVO=new DeviceRelPageReqVO();
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        pageReqVO.setMemberId(userId);
        List<DeviceRelDO> list = deviceRelService.getDeviceRelPage(pageReqVO).getList();
        return success(list);
    }

    @Operation(summary = "新增绑定设备")
    @RequestMapping("addUserDevice")
    @ResponseBody
    public Object addUserDevice(@RequestParam("userId") Integer userId,
                                @RequestParam("deviceName") String deviceName){
        Long count = deviceRelService.getDeviceCountByDeviceName(deviceName);
        if(count>0){
            return error(1,"设备已存在，请勿重复绑定");
        }
        DeviceRelSaveReqVO createReqVO=new DeviceRelSaveReqVO();
        createReqVO.setDeviceName(deviceName);
        createReqVO.setMemberId(userId);
        createReqVO.setActive(true);
        deviceRelService.createDeviceRel(createReqVO);
        return success(true);
    }

    @Operation(summary = "删除绑定设备")
    @RequestMapping("delUserDevice")
    @ResponseBody
    public Object delUserDevice(@RequestParam("id") Integer id){
        deviceRelService.deleteDeviceRel(id);
        return success(true);
    }


    @PostMapping("/createWarnPolicy")
    @Operation(summary = "创建设备消防策略")
    public Object createWarnPolicy(@RequestBody List<WarnPolicySaveReqVO> createReqVO) {
        for(WarnPolicySaveReqVO save:createReqVO){
            String phone = save.getPhone();
            String deviceName = save.getDeviceName();
            WarnPolicyDO byPhone = warnPolicyService.getByPhone(phone,deviceName);
            if(byPhone==null){
                warnPolicyService.createWarnPolicy(save);
            }else{
                save.setId(byPhone.getId());
                warnPolicyService.updateWarnPolicy(save);
            }
        }
        return success(true);
    }


    @PostMapping("/deleteWarnPolicy")
    @Operation(summary = "删除设备消防策略")
    public CommonResult<Boolean> deleteWarnPolicy(@RequestBody List<Long> ids) {
        for (Long id:ids) {
            warnPolicyService.deleteWarnPolicy(id);
        }
        return success(true);
    }


    @PostMapping("/getWarnPolicy")
    @Operation(summary = "获取设备消防策略")
    public Object getWarnPolicy(@Valid @RequestParam("deviceName") String deviceName) {
        WarnPolicyPageReqVO pageReqVO=new WarnPolicyPageReqVO();
        pageReqVO.setDeviceName(deviceName);
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<WarnPolicyDO> warnPolicyPage = warnPolicyService.getWarnPolicyPage(pageReqVO).getList();
        return success(warnPolicyPage);
    }


    @PostMapping("/updateNoticeType")
    @Operation(summary = "修改通知预警事件")
    public Object updateNoticeType(@RequestParam("id") Integer id,
                                   @RequestParam("noticeType") String noticeType) {
        DeviceRelSaveReqVO updateReqVO=new DeviceRelSaveReqVO();
        updateReqVO.setId(id);
        updateReqVO.setNoticeType(noticeType);
        deviceRelService.updateDeviceRel(updateReqVO);
        return success(true);
    }

    @PostMapping("/getNoticeType")
    @Operation(summary = "获取通知预警事件")
    public Object getNoticeType(@RequestParam("id") Integer id) {
        DeviceRelDO deviceRel = deviceRelService.getDeviceRel(id);
        return success(deviceRel.getNoticeType());
    }

    @PostMapping("/getMsgSendList")
    //@Operation(summary = "获取发送的信息历史记录")
    public Object getMsgSendList(@RequestParam("deviceName") String deviceName) {
        PhoneMsgLogPageReqVO vo=new PhoneMsgLogPageReqVO();
        vo.setDeviceName(deviceName);
        vo.setPageNo(1);
        vo.setPageSize(50);
        PageResult<PhoneMsgLogDO> logPage = phoneMsgLogService.getPhoneMsgLogPage(vo);
        List<PhoneMsgLogDO> pageList = logPage.getList();
        return success(pageList);
    }


    @Operation(summary = "语音预警电话")
    @RequestMapping("callPhone")
    @ResponseBody
    public Object callPhone(@RequestParam("phoneNum") String phoneNum,
                            @RequestParam("deviceName") String deviceName){
        LocalDateTime now = LocalDateTime.now();
        PhoneMsgLogSaveReqVO phoneMsg=new PhoneMsgLogSaveReqVO();
        phoneMsg.setPhone(phoneNum);
        phoneMsg.setDeviceName(deviceName);
        phoneMsg.setSendType(false);// 电话通知方式
        phoneMsg.setSendTime(now);
        Map callMsg=new HashMap();
        callMsg.put("warnType","状态");
        callMsg.put("value","可能存在燃气泄漏风险");
        String msg=JSONObject.toJSONString(callMsg);
        try {
            SingleCallByTts.call(phoneNum,msg);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        phoneMsg.setContentValue(msg);
        phoneMsg.setTempCode("TTS_287045128");
        String msgContent="【人工语音提醒】您家的燃气报警器出现状态异常报警，当前设备可能存在燃气泄漏风险，请您注意用气安全！";
        phoneMsg.setContent(msgContent);
        phoneMsgLogService.createPhoneMsgLog(phoneMsg);
        logger.info("人工拨打报警电话信息，报警设备:"+deviceName+",报警电话:"+phoneNum);
        return success(true);
    }



}
